package com.atguigu.srb.core.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.srb.base.result.ResponseEnum;
import com.atguigu.srb.base.util.SrbAssert;
import com.atguigu.srb.core.hfb.FormHelper;
import com.atguigu.srb.core.hfb.HfbConst;
import com.atguigu.srb.core.hfb.RequestHelper;
import com.atguigu.srb.core.pojo.entity.*;
import com.atguigu.srb.core.mapper.LendReturnMapper;
import com.atguigu.srb.core.service.*;
import com.atguigu.srb.core.util.LendNoUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 还款记录表 服务实现类
 * </p>
 *
 * @author Atguigu
 * @since 2024-10-11
 */
@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)//默认RuntimeException回滚
public class LendReturnServiceImpl extends ServiceImpl<LendReturnMapper, LendReturn> implements LendReturnService {

    @Resource
    UserInfoService userInfoService;
    @Resource
    UserAccountService userAccountService;
    @Resource
    LendService lendService;
    @Resource
    LendItemReturnService lendItemReturnService;
    @Resource
    LendItemService lendItemService;
    @Resource
    TransFlowService transFlowService;


    public List<LendReturn> saveLendReturns(Lend lend, UserInfo userInfo) {
        //根据标的期数  生成期数个还款计划，每个还款计划数据存到lend_return表中
        List<LendReturn> lendReturns = new ArrayList<>();
        for (Integer i = 1; i <= lend.getPeriod(); i++) {
            LendReturn lendReturn = new LendReturn();
            lendReturn.setLendId(lend.getId());
            lendReturn.setBorrowInfoId(lend.getBorrowInfoId());
            lendReturn.setReturnNo(LendNoUtils.getReturnNo());
            lendReturn.setUserId(userInfo.getId());//借款人id
            lendReturn.setAmount(lend.getAmount());//申请的借款金额
            lendReturn.setBaseAmount(lend.getInvestAmount());//募资到的计算利息的金额
            lendReturn.setCurrentPeriod(i);//当前期数
            lendReturn.setLendYearRate(lend.getLendYearRate());//年化率是*100的值
            lendReturn.setReturnMethod(lend.getReturnMethod());//还款方式


            lendReturn.setFee(new BigDecimal(0));
            lendReturn.setReturnDate(new DateTime(lend.getLendStartDate()).plusMonths(i).toDate());//第一期 是计息日+1
            lendReturn.setRealReturnTime(null);//本期实际还款日
            lendReturn.setOverdue(null);//还款是否逾期
            lendReturn.setOverdueTotal(new BigDecimal(0));//逾期费用
            lendReturn.setLast(i== lend.getPeriod());//是否是最后一期还款
            lendReturn.setStatus(0);

            lendReturns.add(lendReturn);
        }
        //批量保存
        this.saveBatch(lendReturns);
        return lendReturns;
    }

    public void updateLendReturns(List<LendReturn> lendReturns, List<LendItemReturn> lendItemReturns) {
        lendReturns.forEach(lendReturn -> {
            //查找本期的回款计划集合
            List<LendItemReturn> currntLendItemReturns = lendItemReturns.stream().filter(lendItemReturn ->
                            lendItemReturn.getCurrentPeriod() == lendReturn.getCurrentPeriod())
                    .collect(Collectors.toList());
            BigDecimal principal = currntLendItemReturns.stream().map(LendItemReturn::getPrincipal).reduce((a, b) -> a.add(b)).get();
            BigDecimal interest = currntLendItemReturns.stream().map(LendItemReturn::getInterest).reduce((a, b) -> a.add(b)).get();
            BigDecimal pi = currntLendItemReturns.stream().map(LendItemReturn::getTotal).reduce((a, b) -> a.add(b)).get();
            lendReturn.setPrincipal(principal);
            lendReturn.setInterest(interest);
            lendReturn.setTotal(pi);

        });
        //更新还款计划的本金利息
        this.updateBatchById(lendReturns);
    }

    @Override
    public String borrowReturn(Long id, String token) {
        //1、校验用户状态
        UserInfo userInfo = userInfoService.checkUserStatus(token);

        //2、判断用户账户余额是否足够
        UserAccount userAccount = userAccountService.getUserAccountByUserId(userInfo.getId());
            //根据还款计划期数升序排列，查询未还款的取第一个：如果这个还款计划和用户提交还款计划id一致
        LendReturn lendReturn = this.getOne(Wrappers.lambdaQuery(LendReturn.class)
                .eq(LendReturn::getUserId,userInfo.getId())//查询登录用户的还款计划
                .eq(LendReturn::getStatus,0)//查询未还款的
                .orderByAsc(LendReturn::getCurrentPeriod)//使用期数排序
                .last("limit 1"));//查询第一条

        //判断是否存在未还款的还款计划
        SrbAssert.AssertTrue(lendReturn!=null,
                ResponseEnum.ERROR);

        //判断要还款的还款计划  是否为最近要还的一期还款计划
        SrbAssert.AssertTrue(lendReturn.getId().longValue()==id.longValue(),
               ResponseEnum.BORROW_RETURN_ERROR);

        //判断预期：生成逾期数据
        //当前时间和还款计划还款日比较
        long time = new DateTime().plusDays(1).toDate().getTime();
        long returnTime = new DateTime(lendReturn.getReturnDate()).plusDays(1).toDate().getTime();
        if (time>returnTime){
            //逾期：
            System.out.println("还款逾期");
            //计算逾期金额

        }

        //判断账户余额是否足够
        SrbAssert.AssertTrue(userAccount.getAmount().compareTo(lendReturn.getTotal())>=0,
                ResponseEnum.NOT_SUFFICIENT_FUNDS_ERROR);



        //4、封装自动提交表单参数
        Lend lend = lendService.getById(lendReturn.getLendId());

        Map<String, Object> params = new HashMap<>();
        params.put("agentId",HfbConst.AGENT_ID);
        params.put("agentGoodsName",lend.getTitle());
//        String no = LendNoUtils.getNo();
        params.put("agentBatchNo", id);//批次号
        params.put("fromBindCode",userInfo.getBindCode());//还款人hfb账户 bindCode
        BigDecimal total = lendReturn.getTotal();
        params.put("totalAmt", total);
        params.put("note","第"+lendReturn.getCurrentPeriod()+"期还款");
        params.put("voteFeeAmt","0");//借款人还款时的手续费
        params.put("returnUrl",HfbConst.BORROW_RETURN_RETURN_URL);
        params.put("notifyUrl",HfbConst.BORROW_RETURN_NOTIFY_URL);
        params.put("timestamp",System.currentTimeMillis());

        //还款计划对应的 本期的回款计划列表
        List<LendItemReturn> lendItemReturns = lendItemReturnService.list(Wrappers.lambdaQuery(LendItemReturn.class)
                .eq(LendItemReturn::getLendReturnId,lendReturn.getId()));
        List<Map<String,Object>> maps = lendItemReturns.stream().map(lendItemReturn -> {
          Map<String,Object> map = new HashMap<>();
          map.put("agentProjectCode",lend.getLendNo());//还款项目编号
          // 查询本期回款计划对应的  投资记录
          LendItem lendItem = lendItemService.getById(lendItemReturn.getLendItemId());
          map.put("voteBillNo",lendItem.getLendItemNo());//投资单号：lendItem表中
          UserInfo investUserInfo = userInfoService.getById(lendItem.getInvestUserId());
          map.put("toBindCode",investUserInfo.getBindCode());//投资人bindCode
          map.put("transitAmt",lendItemReturn.getTotal());//还款本金利息
          map.put("baseAmt",lendItemReturn.getPrincipal());//本金
          map.put("benifitAmt",lendItemReturn.getInterest());//利息
          BigDecimal monthRate = lend.getServiceRate().divide(new BigDecimal(1200), 6, BigDecimal.ROUND_DOWN);
            BigDecimal bigDecimal = lendItemReturn.getInterest().multiply(monthRate)
                    //保留小数点后两位
                    .setScale(2, BigDecimal.ROUND_DOWN);
            map.put("feeAmt", bigDecimal);//手续费：对利息计算

            //5、保存投资人回款日志： 虽然不用他保证幂等性
            //但是 回调接口中保存回款日志 回传的参数查询投资回款信息比较繁琐
            TransFlow transFlow = new TransFlow();
            transFlow.setUserId(investUserInfo.getId());//还款人id
            transFlow.setUserName(investUserInfo.getName());//还款人姓名
            transFlow.setTransNo(lendItemReturn.getId().toString());//还款编号
            transFlow.setTransType(4);//4:投资回款
            transFlow.setTransTypeName("投资回款");
            transFlow.setTransAmount(lendItemReturn.getTotal().subtract(bigDecimal));
            transFlow.setStatus(0);
            transFlowService.save(transFlow);
          return map;

        }).collect(Collectors.toList());
        //查询本期还款计划所有的回款计划【 A getCurrentPeriod B getCurrentPeriod】
        params.put("data", JSON.toJSONString(maps));//还款明细数据：投资人汇款金额利息手续费
        params.put("sign", RequestHelper.getSign(params));

        //5、保存日志、幂等性
        //=============保存放款日志
        TransFlow transFlow = new TransFlow();
        transFlow.setUserId(userInfo.getId());//还款人id
        transFlow.setUserName(userInfo.getName());//还款人姓名
        transFlow.setTransNo(id.toString());//还款编号
        transFlow.setTransType(8);//8:还款
        transFlow.setTransTypeName("还款");
        transFlow.setTransAmount(total);
        transFlow.setStatus(0);
        transFlowService.save(transFlow);


        return FormHelper.buildForm(HfbConst.BORROW_RETURN_URL,params);
    }

    @Override
    public String notifyUrl(Map<String, Object> map) {
        //1、验签
        boolean signEquals = RequestHelper.isSignEquals(map);
        if (!signEquals){
            log.error("还款回调失败，验签失败：{}",JSON.toJSONString(map));
            return "fail";
        }
        //2、验证状态码
        if (!"0001".equals(map.get("resultCode"))){
            log.error("还款回调失败，hfb业务处理失败：{}",JSON.toJSONString(map));
            return "fail";
        }

        //3、幂等性、还款金额校验
        String agentBatchNo = map.get("agentBatchNo").toString();
        BigDecimal totalAmt = new BigDecimal(map.get("totalAmt").toString());
        //查询还款日志
        TransFlow transFlow = transFlowService.getOne(Wrappers.lambdaQuery(TransFlow.class)
                .eq(TransFlow::getTransNo, agentBatchNo));
        if (transFlow==null){
            log.error("还款回调失败，还款记录不存在：{}",JSON.toJSONString(map));
            return "fail";
        }
        if (transFlow.getStatus()==1){
            return "success";
        }
            //校验还款金额
        if (transFlow.getTransAmount().compareTo(totalAmt)!=0){
            log.error("还款回调失败，还款金额错误：{}",JSON.toJSONString(map));
            return "fail";
        }

        //4、更新日志状态、还款计划、回款计划状态
        //更新还款日志
        transFlow.setStatus(1);
        transFlow.setMemo(JSON.toJSONString(map));
        transFlow.setUpdateTime(null);
        transFlowService.updateById(transFlow);
        //更新还款计划的状态
        LendReturn lendReturn = this.getById(agentBatchNo);
        lendReturn.setStatus(1);
        lendReturn.setRealReturnTime(new Date());
        lendReturn.setUpdateTime(null);
        this.updateById(lendReturn);


        Lend lend = lendService.getById(lendReturn.getLendId());
        //更新回款计划的状态
        //==查询lendReturn的回款计划
        List<LendItemReturn> lendItemReturns = lendItemReturnService.list(Wrappers.lambdaQuery(LendItemReturn.class)
                .eq(LendItemReturn::getLendReturnId, agentBatchNo));
        //== 更新状态
        lendItemReturns.forEach(lendItemReturn -> {
            lendItemReturn.setStatus(1);
            lendItemReturn.setRealReturnTime(new Date());
            lendItemReturn.setUpdateTime(null);

            //投资人账户入账
            BigDecimal monthRate = lend.getServiceRate().divide(new BigDecimal(1200), 6, BigDecimal.ROUND_DOWN);
            //收益手续费
            BigDecimal bigDecimal = lendItemReturn.getInterest().multiply(monthRate)
                    //保留小数点后两位
                    .setScale(2, BigDecimal.ROUND_DOWN);
            //投资人钱入账
            Long lendReturnId = lendItemReturn.getId();//投资人id
            BigDecimal total = lendItemReturn.getTotal();//投资人总回款
            UserAccount userAccount = userAccountService.getOne(Wrappers.lambdaQuery(UserAccount.class)
                    .eq(UserAccount::getUserId, lendReturnId));
            //原账户+ 本期本息 - 收益手续费
            userAccount.setAmount(userAccount.getAmount().add(total).subtract(bigDecimal));
            userAccountService.updateById(userAccount);


        });
        lendItemReturnService.updateBatchById(lendItemReturns);
        //回款日志：hi款日志保存时的TransNo使用的时回款计划的id
        List<TransFlow> transFlows = transFlowService.list(Wrappers.lambdaQuery(TransFlow.class)
                .in(TransFlow::getTransNo, lendItemReturns.stream()
                        .map(LendItemReturn::getId).collect(Collectors.toList())));
        transFlows.forEach(tf->{
            tf.setUpdateTime(null);
            tf.setStatus(1);
        });
        transFlowService.updateBatchById(transFlows);
        //还款后：扣除借款人账户余额
        Long lendReturnId = lendReturn.getId();//借款人id
        BigDecimal total = lendReturn.getTotal();//还款金额
        //扣除账户提现的金额
        UserAccount userAccount = userAccountService.getOne(Wrappers.lambdaQuery(UserAccount.class)
                .eq(UserAccount::getUserId, lendReturnId));
        userAccount.setAmount(userAccount.getAmount().subtract(total));
        userAccountService.updateById(userAccount);


        return "success";
    }

}
